﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections.ObjectModel;
using System.Globalization;

namespace LostLib.Programs
{
	/// <summary>
	/// Represents InternetMediaType.
	/// </summary>
	[Serializable]
	public class InternetMediaType: IEquatable<InternetMediaType>
	{
		/// <summary>
		/// Constructs InternetMediaType object.
		/// </summary>
		/// <param name="root">Root type.</param>
		/// <param name="subtype">Subtypes.</param>
		public InternetMediaType(string root, IEnumerable<string> subtype)
		{
			Root = root;
			
			Subtype = new ReadOnlyCollection<string>(subtype.ToList());
		}
		/// <summary>
		/// Constructs InternetMediaType object.
		/// </summary>
		/// <param name="root">Root type.</param>
		/// <param name="subtype">Subtypes.</param>
		public InternetMediaType(string root, params string[] subtype)
		{
			Root = root;

			Subtype = new ReadOnlyCollection<string>(subtype);
		}
		/// <summary>
		/// Represents unknown internet media type.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
		public static readonly InternetMediaType Unknown = new InternetMediaType("unknown");

		/// <summary>
		/// Gets root type.
		/// </summary>
		public string Root { get; private set; }
		/// <summary>
		/// Gets subtype.
		/// </summary>
		public ReadOnlyCollection<String> Subtype { get; private set; }

		/// <summary>
		/// Parses string representation of InternetMediaType into object.
		/// </summary>
		/// <param name="value">String representation.</param>
		/// <param name="result">Resulting object.</param>
		/// <returns>If conversion is OK returns true else returns false.</returns>
		public static bool TryParse(string value, out InternetMediaType result)
		{
			result = null;
			if (value == null) return false;

			int index = value.IndexOf('/');
			if (index < 0) return false;

			result = new InternetMediaType(value.Substring(0, index), value.Substring(index + 1).Split('-'));
			return true;
		}
		
		/// <summary>
		/// Returns string representation of this InternetMediaType.
		/// </summary>
		/// <returns>String representation.</returns>
		public override string ToString()
		{
			return string.Format(CultureInfo.InvariantCulture, "{0}/{1}", Root, string.Join("-", Subtype.ToArray()));
		}

		#region IEquatable<InternetMediaType> Members
		/// <summary>
		/// Compares this instance to other object.
		/// </summary>
		/// <param name="obj">Object to compare to.</param>
		/// <returns>True if instances equals.</returns>
		public override bool Equals(object obj)
		{
			var other = obj as InternetMediaType;
			return other == null? false: this.Equals(other);
		}
		/// <summary>
		/// Compares this instance to other InternetMediaType.
		/// </summary>
		/// <param name="other">InternetMediaType to compare to.</param>
		/// <returns>True if instances equals.</returns>
		public bool Equals(InternetMediaType other)
		{
			if (other == null) return false;
			if (this.Root != other.Root) return false;
			if (Subtype.Count != other.Subtype.Count) return false;
			for (int i = 0; i < Subtype.Count; i++)
				if (Subtype[i] != other.Subtype[i]) return false;
			return true;
		}

		#endregion
		/// <summary>
		/// Gets hash code for this instance.
		/// </summary>
		/// <returns>Hash code.</returns>
		public override int GetHashCode()
		{
			int result = Root.GetHashCode();
			foreach (var subtype in Subtype) result ^= subtype.GetHashCode();
			return result;
		}
	}

	/// <summary>
	/// InternetMediaType extensions.
	/// </summary>
	public static class InternetMediaTypeExtensions
	{
		/// <summary>
		/// Returns Internet media type for given file system entry.
		/// </summary>
		/// <param name="entry">Entry to get media type for.</param>
		/// <returns>InternetMediaType object.</returns>
		public static InternetMediaType GetMediaType(this FileSystemInfo entry)
		{
			switch (entry.Extension)
			{
			case ".msi":
				return new InternetMediaType("application", "x", "msi");
			default:
				return InternetMediaType.Unknown;
			}
		}
	}
}
